package com.alibaba.markovdemo.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.markovdemo.BO.MultiAIInfo;
import com.alibaba.markovdemo.BO.TestCaseInput;
import com.alibaba.markovdemo.common.AjaxResult;
import com.alibaba.markovdemo.engine.AI.GACaseGenerator.GenerateController;
import com.alibaba.markovdemo.engine.MultiAIPlusController;
import com.alibaba.markovdemo.engine.SingleController;
import com.alibaba.markovdemo.engine.stages.ResultStatus;
import com.alibaba.markovdemo.engine.util.Toolkit;
import com.alibaba.markovdemo.entity.*;
import com.alibaba.markovdemo.service.*;
import com.google.gson.Gson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;


/**
 * 说明:用例管理页
 * 1.包含了用例新建/查询/克隆/执行/回归等功能
 * 2.测试环境的选择此处采用mock方式,当然,真实测试场景下仍需要用户去实现真实环境的获取.
 */

@Controller
@RequestMapping(value = "/api")
public class TestcaseController {
  private static final Logger logger = LoggerFactory.getLogger(TestcaseController.class);

  @Autowired
  private GotReportsService gotReportsService;
  @Autowired
  private TestcaseService testcaseService;
  @Autowired
  private SingleController singleController;
  @Autowired
  private PipelineService pipelineService;
  @Autowired
  MultiAIPlusController multiAIController;
  @Autowired
  private TestcaseSnapsService testcaseSnapsService;
  @Autowired
  private GenerateController generateController;
  @Autowired
  private CaseGenerateTaskService caseGenerateTaskService;
  @Autowired
  private CaseAccuracyService caseAccuracyService;


  @Autowired
  private HttpServletRequest request;

  /**
   * 功能:获取测试场景下的分页用例列表
   *
   * @param scenarioId
   * @param pageNo
   * @return
   */
  @RequestMapping(value = "/getTestcaseByScenario", method = RequestMethod.GET)
  @ResponseBody
  public AjaxResult getTestcaseByScenario(Long scenarioId, Integer pageNo) {
    try {
      if (pageNo == null || pageNo == 0) {
        pageNo = 1;
      }

      List<GotTestCase> caseList = testcaseService.getVisibleTestcaseByScenario(scenarioId, pageNo);

      JSONObject res = new JSONObject();
      res.put("testCaseList", caseList);

      Integer allCaseNum = testcaseService.getAllCaseNum(scenarioId);
      res.put("allNumber", allCaseNum);

      return AjaxResult.succResult(res);

    } catch (Exception e) {
      return AjaxResult.errorResult(e.getMessage());
    }
  }

  /**
   * 功能:获取用例调试页的布局json
   * 该布局json描述了用例依赖的测试数据类型/测试流程等,与用例数据结合后形成完整的测试流程+测试数据.
   *
   * @param scenarioId
   * @return
   */
  @RequestMapping(value = "/getLayoutJson", method = RequestMethod.GET)
  @ResponseBody
  public AjaxResult getLayout(Long scenarioId) throws IOException {

    try {
      return AjaxResult.succResult(singleController.getLayout(scenarioId));
    } catch (Exception e) {
      return AjaxResult.errorResult(e.getMessage());
    }
  }

  /**
   * 功能:获取测试用例数据
   * 测试用例数据与布局json结合后形成完整的测试流程+测试数据
   *
   * @param testCaseId
   * @return
   */
  @RequestMapping(value = "/getTestCase", method = RequestMethod.GET)
  @ResponseBody
  public AjaxResult getTestCase(Long testCaseId) {
    try {
      return AjaxResult.succResult(testcaseService.getTestCaseById(testCaseId));
    } catch (Exception e) {
      return AjaxResult.errorResult(e.getMessage());
    }
  }

  /**
   * 功能:
   * 获取指定分组scenarioId下的用例分组列表
   * 注:1.该接口可扩展为指定场景和用例分支,但在demo中我们暂不考虑用例分支的情况
   * 2.用例分组的在回归测试中使用,即用户可选择某类分组进行回归.
   *
   * @param appId
   * @param scenarioId
   * @return
   */
  @RequestMapping(value = "/getCaseGroupListByBranch", method = RequestMethod.GET)
  @ResponseBody
  public AjaxResult getTestCaseGroupList(Long appId, Long scenarioId) {
    try {
      Map<String, Object> map = new HashMap();
      List<String> caseGroupList = testcaseService.getCaseGroupByScenarioId(scenarioId);
      int allNumber = caseGroupList.size();
      map.put("allNumber", allNumber);
      map.put("caseGroupList", caseGroupList);
      return AjaxResult.succResult(map);
    } catch (Exception e) {
      return AjaxResult.errorResult(e.getMessage());
    }
  }

  /**
   * 功能:测试用例保存
   *
   * @param testCase
   * @return
   */
  @RequestMapping(value = "/saveTestCase", method = RequestMethod.POST)
  @ResponseBody
  public AjaxResult saveTestCase(@RequestBody TestCaseInput testCase) {
    try {
      return AjaxResult.succResult(testcaseService.saveTestCase(testCase));
    } catch (Exception e) {
      return AjaxResult.errorResult(e.getMessage());
    }
  }

  /**
   * 功能:测试用例删除
   * 注意:此处为逻辑删
   *
   * @param testCaseId
   * @return
   */
  @RequestMapping(value = "/deleteTestCase", method = RequestMethod.DELETE)
  @ResponseBody
  public AjaxResult deleteTestCase(Long testCaseId) {
    try {
      testcaseService.deleteTestCase(testCaseId);
      return AjaxResult.succResultMessage("delete success.");
    } catch (Exception e) {
      return AjaxResult.errorResult(e.getMessage());
    }
  }


  /**
   * 功能:测试用例执行
   * 根据前端传来的测试用例数据,后端初始化执行插件,按照预定义的执行流程依次运行,最终将结果返回给前端
   * 注意:
   * 此处demo采用同步执行机制,便于描述.但在实际中往往我们采用异步执行方式.后续开源如有需要,将逐步开放基于zk的异步执行调用方式
   *
   * @param testCase
   * @return
   * @throws Exception
   */
  @RequestMapping(value = "/runSingleTestCase", method = RequestMethod.POST)
  @ResponseBody
  public AjaxResult runSingleTestCase(@RequestBody TestCaseInput testCase) throws Exception {
    try {
      GotPipeline gotPipeline = pipelineService.getPipeline(testCase.getScenarioId());
      JSONObject pipelineJsonObj = JSON.parseObject(gotPipeline.getPipeline());
      return AjaxResult.succResult(singleController.runCase(testCase, pipelineJsonObj));
    } catch (Exception e) {
      return AjaxResult.errorResult(e.getMessage());
    }

  }

  /**
   * 功能:功能回归测试
   * 注意:
   * 此处demo采用了最基本和最普适的caseBYcase回归机制,便于描述平台思想.后续开源将逐步开放智能回归的高效回归方式.
   *
   * @param multiAIInfo
   * @return
   * @throws IOException
   */
  @RequestMapping(value = "/runMultiAITestCase", method = RequestMethod.POST)
  @ResponseBody
  public AjaxResult runMultiAITestCase(@RequestBody MultiAIInfo multiAIInfo) throws IOException {
    try {
      Gson gson = new Gson();
      System.out.print(gson.toJson(multiAIInfo));
      multiAIController.runIntelligent(multiAIInfo);
      return AjaxResult.succResult("智能回归测试任务提交成功,请进入执行历史页面查看!");
    } catch (Exception e) {
      return AjaxResult.errorResult(e.getMessage());
    }
  }

  /**
   * 功能:获取测试报告列表
   * 说明:执行一次回归测试后会产生一份测试报告.
   *
   * @param appId
   * @param scenarioId
   * @param pageId
   * @param pageSize
   * @return
   */
  @RequestMapping(value = "/getReportList", method = RequestMethod.GET)
  @ResponseBody
  public AjaxResult getReportList(Long appId, Long scenarioId, Integer pageId, Integer pageSize) {

    try {

      Map<String, Object> map = new HashMap();
      Integer allNumber = 0;
      List<GotReports> reportsList;
      Integer fromRow = (pageId - 1) * pageSize;
      allNumber = gotReportsService.getIdsByScenarioId(scenarioId).size();
      reportsList = gotReportsService.getVisibleByScenarioIdPage(scenarioId, fromRow, pageSize);
//            for (GotReports gotReports : reportsList){
//                gotReports.setGmtCreateStr(sdf.format(gotReports.getGmtCreate()));
//                gotReports.setGmtModifiedStr(sdf.format(gotReports.getGmtModified()));
//            }
      map.put("allNumber", allNumber);
      map.put("reportsList", reportsList);
      return AjaxResult.succResult(map);
    } catch (Exception e) {
      return AjaxResult.errorResult(e.getMessage());
    }
  }

  /**
   * 功能:获取测试报告详情
   * 说明:详情包含了测试执行时间/用例快照列表/执行用例数/回归的交付物信息等..
   *
   * @param testReportId
   * @param pageId
   * @param pageSize
   * @param status
   * @return
   */
  @RequestMapping(value = "/getReportTestCase", method = RequestMethod.GET)
  @ResponseBody
  public AjaxResult getReportTestCase(Long testReportId, Integer pageId, Integer pageSize, String status) {
    try {
      Map map = new HashMap();
      Integer runNumber;
      Integer sucessNumber = 0;
      Integer failureNumber = 0;
      Integer allStatusNumber = 0;
      //获取执行人,回归时间,回归名称等
      GotReports gotReports = gotReportsService.findById(testReportId);
      String reportName = gotReports.getReportName();
      String user = gotReports.getUser();
      Date gmtCreate = gotReports.getGmtCreate();
      Date gmtModified = gotReports.getGmtModified();
      int caseNum = gotReports.getCaseNum();
      double seconds = (double) (gmtModified.getTime() - gmtCreate.getTime());
      //统计用例数
      List<GotTestcaseSnaps> testcaseList = testcaseSnapsService.getReportTestcaseList(testReportId);
      runNumber = testcaseList.size();
      for (GotTestcaseSnaps testcase : testcaseList) {
        if (!testcase.getStatus().equals(ResultStatus.SUCCESS.name())) {
          failureNumber++;
        } else {
          sucessNumber++;
        }
      }
      Integer fromRow = (pageId - 1) * pageSize;
      List<GotTestcaseSnaps> testcaseListPage = testcaseSnapsService.getReportTestcaseListPage(testReportId, fromRow, pageSize, status);


      List<Long> caseids = new ArrayList<>();
      for (GotTestcaseSnaps gotTestcaseSnaps : testcaseListPage) {
        caseids.add(gotTestcaseSnaps.getTestcaseId());
      }

      if (status == null) {
        allStatusNumber = runNumber;
      } else if (status.contains("ERROR")) {
        allStatusNumber = failureNumber;
      } else if (status.contains("SUCCESS")) {
        allStatusNumber = runNumber - failureNumber;
      } else {
        allStatusNumber = 0;
      }

      map.put("status", gotReports.getStatus());
      map.put("allNumber", caseNum);
      map.put("runNumber", runNumber);
      map.put("sucessNumber", sucessNumber);
      map.put("allStatusNumber", allStatusNumber);
      map.put("failureNumber", failureNumber);
      map.put("testcaseList", testcaseListPage);
      map.put("gmtCreate", gmtCreate);
      map.put("gmtModified", gmtModified);
      map.put("reportName", reportName);
      map.put("user", user);
      map.put("timeGap", Toolkit.changeCostFormat((long) seconds));
      map.put("imageName", gotReports.getImageName());

      return AjaxResult.succResult(map);
    } catch (Exception e) {
      return AjaxResult.errorResult(e.getMessage());
    }
  }

  @RequestMapping(value = "/getReport", method = RequestMethod.GET)
  @ResponseBody
  public AjaxResult getReport(Long testReportId) {

    try {
      GotReports gotReport = gotReportsService.findById(testReportId);

      JSONObject analysisObj = JSONObject.parseObject(gotReport.getAnalysis());
      return AjaxResult.succResult(analysisObj);
    } catch (Exception e) {
      return AjaxResult.errorResult(e.getMessage());
    }
  }


  @RequestMapping(value = "/startAICaseGenerator", method = RequestMethod.POST)
  @ResponseBody
  public AjaxResult startAICaseGenerator(@RequestBody JSONObject params) {

    try {
      String caseId = params.getString("caseId");
      Long scenarioId = params.getLong("scenarioId");
      String fieldConf = params.getString("fieldConf");
      HashMap<String, Object> envInfo = (HashMap<String, Object>) params.get("envInfo");
      JSONObject res = new JSONObject();
      res.put("taskId", generateController.startGenerateTask(Long.parseLong(caseId), scenarioId, fieldConf, envInfo));
      return AjaxResult.succResult(res);
    } catch (Exception e) {
      return AjaxResult.errorResult(e.getMessage());
    }
  }


  @RequestMapping(value = "/getDefaultGeneConf", method = RequestMethod.GET)
  @ResponseBody
  public AjaxResult getDefaultGeneConf(Long scenarioId) {

    try {
      //just mock
      String defaultConf = "{\n" +
          "\t\"neg_tesla_key\": [\n" +
          "\t\t\"ad_id\"\n" +
          "\t],\n" +
          "\t\"field_setting\": {\n" +
          "\t\t\"search_key\": [\"key1\", \"key2\", \"key3\", \"key4\", \"key5\", \"key6\", \"key7\"],\n" +
          "\t\t\"match_level\": [1, 2, 3, 4, 5, 6],\n" +
          "\t\t\"user_type\": [\"type1\", \"type2\", \"type3\", \"type4\", \"type5\"],\n" +
          "\t\t\"top_num\": [10, 11, 12, 13, 14, 15],\n" +
          "\t\t\"use_feature\": [true, false],\n" +
          "\t\t\"other1\": [\"0\", \"1\", \"2\", \"3\"],\n" +
          "\t\t\"other2\": [\"0\", \"1\", \"2\", \"3\"]\n" +
          "\t}\n" +
          "}";

      return AjaxResult.succResult(defaultConf);
    } catch (Exception e) {
      return AjaxResult.errorResult(e.getMessage());
    }
  }


  @RequestMapping(value = "/getGeneratorTaskDetail", method = RequestMethod.GET)
  @ResponseBody
  public AjaxResult getGeneratorTaskDetail(Long taskId) {

    try {
      GotCaseGenerateTask task = caseGenerateTaskService.findTaskById(taskId);
      JSONObject res = new JSONObject();
      res.put("task", task);
      try {
        JSONArray cases = JSONObject.parseObject(task.getTaskResult()).getJSONArray("valid_ids");


        JSONArray caseArray = new JSONArray();
        for (Object caseId : cases) {
          GotTestCase testcase = testcaseService.findById(Long.parseLong(caseId.toString()));
          GotCaseAccuracy accuracy = caseAccuracyService.getLastedByCaseId(Long.parseLong(caseId.toString()));
          JSONObject temp = new JSONObject();
          temp.put("caseid", testcase.getId());
          temp.put("caseName", testcase.getDescription());
          temp.put("accuracy", accuracy.getCovLine());
          caseArray.add(temp);
        }
        res.put("validCaseInfo", caseArray);
      } catch (Exception e) {

      }

      return AjaxResult.succResult(res);
    } catch (Exception e) {
      return AjaxResult.errorResult(e.getMessage());
    }
  }


  @RequestMapping(value = "/changeVisible", method = RequestMethod.GET)
  @ResponseBody
  public AjaxResult changeVisible(Long caseId) {

    try {
      //just mock
      GotTestCase testCase = testcaseService.findById(caseId);
      testCase.setIsVisible(0);
      testcaseService.update(testCase);

      return AjaxResult.succResult(true);
    } catch (Exception e) {
      return AjaxResult.errorResult(e.getMessage());
    }
  }


  @RequestMapping(value = "/getLastGenerateTask", method = RequestMethod.GET)
  @ResponseBody
  public AjaxResult getLastGenerateTask(Long scenarioId) {

    try {
      //just mock
      GotCaseGenerateTask task = caseGenerateTaskService.getLastGenerateTask(scenarioId);
      if (task != null) {
        return AjaxResult.succResult(task.getId());
      } else {
        return AjaxResult.errorResult(null);
      }
    } catch (Exception e) {
      return AjaxResult.errorResult(e.getMessage());
    }
  }


}
